package stdio.costa.imoca.spi;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Scope;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import stdio.costa.imoca.model.Image;
import stdio.costa.imoca.utils.ImageUtils;

import com.aliyun.openservices.ClientConfiguration;
import com.aliyun.openservices.ClientException;
import com.aliyun.openservices.oss.OSSClient;
import com.aliyun.openservices.oss.OSSException;
import com.aliyun.openservices.oss.model.OSSObject;
import com.aliyun.openservices.oss.model.ObjectMetadata;

/**
 * 这个类使用的前提是，在阿里云中已创建好了相应的bucket， 所以程序不去调用创建bucket的代码。把运维工作从代码中分离出去。
 * @author 张桂林
 */
@Service
@Scope(BeanDefinition.SCOPE_SINGLETON)
public class AliyunOssAdapter implements ImageServiceSpi {

	private static final Logger logger = LoggerFactory.getLogger(AliyunOssAdapter.class);
	
	@Value("#{properties['aliyun.oss.access.id']}")
	private String accessId;
	
	@Value("#{properties['aliyun.oss.access.key']}")
	private String accessKey;

	@Value("#{properties['aliyun.oss.endpoint']}")
	private String endpoint;
	
	@Value("#{properties['aliyun.oss.bucket.name']}")
	private String bucketName;
	
	@Value("#{properties['aliyun.oss.publish.domain']}")
	private String publishDomain;
	
	@Value("#{properties['aliyun.oss.bucket.directories.medium']}")
	private String mediumDirectory;
	
	@Value("#{properties['aliyun.oss.bucket.directories.thumbnail']}")
	private String thumbnailDirectory;
	
	@Value("#{properties['aliyun.oss.bucket.directories.large']}")
	private String largeDirectory;
	
	@Value("#{properties['image.width.medium']}")
	private int mediumWidth;
	
	@Value("#{properties['image.height.medium']}")
	private int mediumHeight;
	
	@Value("#{properties['image.width.thumbnail']}")
	private int thumbnailWidth;
	
	@Value("#{properties['image.height.thumbnail']}")
	private int thumbnailHeight;
	
	@Value("#{properties['image.width.large']}")
	private int largeWidth;
	
	@Value("#{properties['image.height.large']}")
	private int largeHeight;
	
	@PersistenceContext
	private EntityManager entityManager;
	
	@Override
	@Async
	@Transactional
	public void create(Image image) {
		ClientConfiguration config = new ClientConfiguration();
		OSSClient client = new OSSClient(endpoint, accessId, accessKey, config);
		try {
			image.setLargeUrl(upload(image, client, largeDirectory, largeWidth, largeHeight));
			image.setMediumUrl(upload(image, client, mediumDirectory, mediumWidth, mediumHeight));
			image.setThumbnailUrl(upload(image, client, thumbnailDirectory, thumbnailWidth, thumbnailHeight));
			entityManager.merge(image);
		} catch (Exception e) {
			logger.error("Failed to upload file to aliyun.", e);
		}
	}

	protected String upload(Image image, OSSClient client, String directory, int width, int height) throws OSSException, ClientException, IOException {
		ObjectMetadata metadata = new ObjectMetadata();
		metadata.setContentType(image.getMediaType());
		byte[] data = ImageUtils.zoom(image.getData(), width, height);
		metadata.setContentLength(data.length);
		String path = new StringBuilder(directory).append(image.getUuid()).toString();
		client.putObject(bucketName, path, new ByteArrayInputStream(data), metadata);
		return new StringBuilder(publishDomain).append(path).toString();
	}

	@Override
	public InputStream get(String uuid, Integer width, Integer height) {
		ClientConfiguration config = new ClientConfiguration();
		OSSClient client = new OSSClient(endpoint, accessId, accessKey, config);
		try {
			logger.info("Downloading image {}", uuid);
			OSSObject object = client.getObject(bucketName, uuid);
			byte[] image = ImageUtils.zoom(object.getObjectContent(), width, height);
			return new ByteArrayInputStream(image);
		} catch (OSSException e) {
			logger.error("Failed to download image: {}", uuid);
			logger.error("Failure reason:", e);
		} catch (ClientException e) {
			logger.error("Failed to download image: {}", uuid);
			logger.error("Failure reason:", e);
		} catch (IOException e) {
			logger.error("Failed to download image: {}", uuid);
			logger.error("Failure reason:", e);
		}
		return null;
	}
}
